package com.szholly.utils.session.provider;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import com.szholly.utils.session.CurrentSessionHelper;
import com.szholly.utils.session.SessionFactory;
import com.szholly.utils.session.id.ISessionIdGenerator;
import com.szholly.utils.session.provider.cache.ISessionCache;
import com.szholly.utils.util.Constants;
import com.szholly.utils.util.RequestUtils;

/**
 * 使用Memcached分布式缓存实现Session
 */
public class CacheSessionProvider implements ISessionProvider, InitializingBean {

	private ISessionCache sessionCache;
	private ISessionIdGenerator sessionIdGenerator;
	private int sessionTimeout = 30;

	public static final String CURRENT_SESSION = "_current_session";
	public static final String CURRENT_SESSION_ID = "_current_session_id";
	
	public CacheSessionProvider(){
		sessionCache = SessionFactory.getSessionCache();
		sessionIdGenerator = SessionFactory.getSessionIdGenerator();
	}
	
	/**
	 * 设置session过期时间
	 * 
	 * @param sessionTimeout 分钟
	 */
	public void setSessionTimeout(int sessionTimeout) {
		Assert.isTrue(sessionTimeout > 0);
		this.sessionTimeout = sessionTimeout;
	}

	@SuppressWarnings("unchecked")
	public Serializable getAttribute(HttpServletRequest request, String name) {
		// 为了避免同一个请求多次获取缓存session，所以将缓存session保存至request中
		Map<String, Serializable> session = (Map<String, Serializable>) request
				.getAttribute(CURRENT_SESSION);
		if (session != null) {
			return session.get(name);
		}

		String root = (String) request.getAttribute(CURRENT_SESSION_ID);
		if (root == null) {
			root = RequestUtils.getRequestedSessionId(request);
		}
		if (StringUtils.isBlank(root)) {
			request.setAttribute(CURRENT_SESSION, new HashMap<String, Serializable>());
			return null;
		}
		session = sessionCache.getSession(root);
		if (session != null) {
			request.setAttribute(CURRENT_SESSION_ID, root);
			request.setAttribute(CURRENT_SESSION, session);
			return session.get(name);
		} else {
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public void setAttribute(HttpServletRequest request,
			HttpServletResponse response, String name, Serializable value) {
		Map<String, Serializable> session = (Map<String, Serializable>) request
				.getAttribute(CURRENT_SESSION);
		String root;
		if (session == null) {
			root = RequestUtils.getRequestedSessionId(request);
			if (root != null && root.length() == 32) {
				session = sessionCache.getSession(root);
			}
			if (session == null) {
				session = new HashMap<String, Serializable>();
				do {
					root = sessionIdGenerator.get();
				} while (sessionCache.exist(root));
				response.addCookie(createCookie(request, root));
			}
			request.setAttribute(CURRENT_SESSION, session);
			request.setAttribute(CURRENT_SESSION_ID, root);
		} else {
			root = (String) request.getAttribute(CURRENT_SESSION_ID);
			if (root == null) {
				do {
					root = sessionIdGenerator.get();
				} while (sessionCache.exist(root));
				response.addCookie(createCookie(request, root));
				request.setAttribute(CURRENT_SESSION_ID, root);
			}
		}
		session.put(name, value);
		sessionCache.setSession(root, session, sessionTimeout);
	}

	public String getSessionId(HttpServletRequest request,
			HttpServletResponse response) {
		String root = (String) request.getAttribute(CURRENT_SESSION_ID);
		if (root != null) {
			return root;
		}
		root = RequestUtils.getRequestedSessionId(request);
		if (root == null || root.length() != 32 || !sessionCache.exist(root)) {
			do {
				root = sessionIdGenerator.get();
			} while (sessionCache.exist(root));
			sessionCache.setSession(root, new HashMap<String, Serializable>(),
					sessionTimeout);
			response.addCookie(createCookie(request, root));
		}
		request.setAttribute(CURRENT_SESSION_ID, root);
		return root;
	}

	public void logout(HttpServletRequest request, HttpServletResponse response) {
		request.removeAttribute(CURRENT_SESSION);
		request.removeAttribute(CURRENT_SESSION_ID);
		String root = RequestUtils.getRequestedSessionId(request);
		if (!StringUtils.isBlank(root)) {
			sessionCache.clear(root);
			Cookie cookie = createCookie(request, null);
			cookie.setMaxAge(0);
			response.addCookie(cookie);
		}
	}

	private Cookie createCookie(HttpServletRequest request, String value) {
		Cookie cookie = new Cookie(Constants.JSESSION_COOKIE, value);
		String ctx = request.getContextPath();
		cookie.setPath(StringUtils.isBlank(ctx) ? "/" : ctx);
		return cookie;
	}

	/**
	 * 获取IP
	 * 
	 * @return
	 */
	public String getIp() {
		return HttpSessionProvider.getIpAddr(CurrentSessionHelper.getCurrentHttpServletRequest());
	}
	
	/**
	 * 获取当前登录的用户名
	 * @return
	 */
	@Override
	public String getUserName(){
		return (String)this.getObject(UserName);
	}
	
	@Override
	public String getUserName(HttpSession sesison) {
		return (String)this.getObject(sesison,UserName);
	}

	/**
	 * 设置当前登录的用户名
	 * @param userName
	 */
	@Override
	public void setUserName(String userName){
		this.setObject(UserName, userName);
	}
	
	@Override
	public void setUserName(HttpSession sesison, String userName) {
		this.setObject(sesison,UserName, userName);
	}

	/**
	 * 获取当前登录的密码
	 * @return
	 */
	@Override
	public String getPassword(){
		return (String)this.getObject(Password);
	}
	
	@Override
	public String getPassword(HttpSession sesison) {
		return (String)this.getObject(sesison,Password);
	}

	/**
	 * 设置当前登录的密码
	 * @param password
	 */
	@Override
	public void setPassword(String password){
		this.setObject(Password, password);
	}
	
	@Override
	public void setPassword(HttpSession sesison, String password) {
		this.setObject(sesison,Password, password);
	}

	/**
	 * 获取当前的系统名称
	 * @return
	 */
	@Override
	public String getOrgName(){
		return (String)this.getObject(OrgName);
	}
	
	@Override
	public String getOrgName(HttpSession sesison) {
		return (String)this.getObject(sesison,OrgName);
	}

	/**
	 * 设置当前的系统名称
	 * @param systemName
	 */
	@Override
	public void setOrgName(String systemName){
		this.setObject(OrgName, systemName);
	}
	
	@Override
	public void setOrgName(HttpSession sesison, String orgName) {
		this.setObject(sesison, OrgName, orgName);
	}

	@Override
	public Object getObject(String key) {
        HttpSession session = CurrentSessionHelper.getCurrentSession();
		return getObject(session,key);
	}

	@Override
	public Object getObject(HttpSession session, String key) {
		if(session==null){
			return null;
		}
		String sessionID = session.getId();
		Map<String, Serializable> sessionValues = sessionCache.getSession(sessionID);
		if(sessionValues.containsKey(key)){
			return sessionValues.get(key);
		}
		else{
			return null;
		}
	}

	@Override
	public void setObject(String key, Object obj) {
		HttpSession session = CurrentSessionHelper.getCurrentSession();
		setObject(session,key,obj);
	}

	@Override
	public void setObject(HttpSession session, String key, Object obj) {
		if(session==null){
			return;
		}
		
		String sessionID = session.getId();
		Map<String, Serializable> sessionValues = sessionCache.getSession(sessionID);
		if(sessionValues.containsKey(key)){
			sessionValues.remove(key);
		}
		sessionValues.put(key, (Serializable) obj);
	}

	@Override
	public void remove(String key) {
		HttpSession session = CurrentSessionHelper.getCurrentSession();
		remove(session,key);
	}
	
	@Override
	public void remove(HttpSession session, String key) {
		if(session==null){
			return;
		}
		
		String sessionID = session.getId();
		Map<String, Serializable> sessionValues = sessionCache.getSession(sessionID);
		if(sessionValues.containsKey(key)){
			sessionValues.remove(key);
		}
	}

	/*
	 * 清除Session中对象
	 */
	@Override
	public void clear(){
		HttpSession session = CurrentSessionHelper.getCurrentSession();
		clear(session);
	}

	@Override
	public void clear(HttpSession session) {
		if(session==null){
			return;
		}
		
		String sessionID = session.getId();
		sessionCache.clear(sessionID);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
	}
}